package com.wzh.yuvwater.encoder;

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.MediaRecorder;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Process;
import android.support.annotation.RequiresApi;
import android.util.Log;

import com.wzh.yuvwater.muxer.MuxerData;
import com.wzh.yuvwater.muxer.MuxerManager;
import com.wzh.yuvwater.utils.Logger1;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.ArrayBlockingQueue;

/**
 * 描述：音频编码管理
 * <p>
 * 邮箱：admin@bzchao.com
 */
@RequiresApi(api = Build.VERSION_CODES.M)
public class AudioCodecManager {
    private static AudioCodecManager sInstance;
    private static String TAG = "AudioCodecManager";

    private ArrayBlockingQueue<byte[]> frameBytes;

    private AudioRecord mRecorder;
    private MediaCodec mMediaCodec;
    private MediaFormat mMediaFormat;
    private int mBufferSize;

    private Handler mHandler;
    private HandlerThread mHandlerThread;

    private volatile boolean isStart = false;
    //通过加锁的方式解决MediaCodec编码器已关闭，视频还在处理导致的异常
    //加锁逻辑:处理视频加锁，操作MediaCodec加锁（保证MediaCodec处理视频正常）
    private Object isStartLock = new Object();

    private AudioCodecManager() {
    }

    public static AudioCodecManager getInstance() {
        if (sInstance == null) {
            synchronized (AudioCodecManager.class) {
                if (sInstance == null) {
                    sInstance = new AudioCodecManager();
                }
            }
        }
        return sInstance;
    }

    public void startRecording() {
        try {
            frameBytes = new ArrayBlockingQueue<>(100);
            prepare();
            mHandler.post(() -> start());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void addFrameData(byte[] data) {
        if (isStart && !PauseManager.getInstance().isPause()) {
            //offer添加一个数据
            boolean isOffer = frameBytes.offer(data);
            if (!isOffer) {
                //如果添加不成功，则移除一个数据后再添加
                frameBytes.poll();
                frameBytes.offer(data);
            }
        }
    }

    private void runRecord() {
        int readSize = Math.min(mBufferSize, 1024 * 2);
        while (isStart) {
            try {
                // new一个byte数组用来存一些字节数据，大小为缓冲区大小
                byte[] audioData = new byte[readSize];
                int length = mRecorder.read(audioData, 0, readSize);
                if (length > 0) {
                    addFrameData(audioData);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
            }
        }
    }

    /**
     * 暂停录制
     */
    public void pauseMediaCodec() {
        if (!isStart) {
            Logger1.i(TAG, "MediaCodec: isn't started");
            return;
        }
        frameBytes.clear();
    }

    public void stopRecording() {
        isStart = false;
        frameBytes.clear();
        sInstance = null;
        mHandler = null;
        mHandlerThread.quit();
        stop();
    }

    public void prepare() throws IOException {
        mHandlerThread = new HandlerThread("AudioCodecThread");
        mHandlerThread.start();
        mHandler = new Handler(mHandlerThread.getLooper());

        //录音设置
        int bitRate = 128000;   //比特率9600
        int sampleRate = 8000;  //采样率，默认44.1k
        int channelCount = 2;   //音频采样通道，默认2通道
        int audioFormat = AudioFormat.ENCODING_PCM_16BIT;     //设置采样数据格式，默认16比特PCM
        String mimeType = MediaFormat.MIMETYPE_AUDIO_AAC;
        //通道设置，默认立体声
        int channelConfig = channelCount == 1 ? AudioFormat.CHANNEL_IN_MONO : AudioFormat.CHANNEL_OUT_STEREO;
        //音频录制相关
        mBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
        //mBufferSize = getAudioBufferSize(channelConfig, audioFormat);
        Logger1.i(TAG, "mBufferSize:" + mBufferSize);

        //音频编码相关
        mMediaFormat = MediaFormat.createAudioFormat(mimeType, sampleRate, channelCount);
        mMediaFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
        mMediaFormat.setInteger(MediaFormat.KEY_CHANNEL_MASK, AudioFormat.CHANNEL_IN_MONO);
        mMediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
        //配置最大输入大小，这里配置的是前面起算的大小的2倍
        //mMediaFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, mBufferSize * 2);

        mMediaCodec = MediaCodec.createEncoderByType(mimeType);
        mMediaCodec.configure(mMediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
        mMediaCodec.setCallback(getCodeConfig(), mHandler);

        mRecorder = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRate, channelConfig, audioFormat, mBufferSize);
    }

    private MediaCodec.Callback getCodeConfig() {
        return new MediaCodec.Callback() {
            @Override
            public void onInputBufferAvailable(MediaCodec codec, int inputIndex) {
                byte[] outData;
                try {
                    //注意有isStartLock锁，此处阻塞不应该在锁之中！
                    byte[] bytes = frameBytes.take();
                    outData = bytes.clone();
                } catch (InterruptedException e) {
                    return;
                }

                synchronized (isStartLock) {
                    if (!isStart) {
                        return;
                    }
                    final ByteBuffer inputBuffer = codec.getInputBuffer(inputIndex);
                    inputBuffer.clear();
                    inputBuffer.put(outData);

                    //通过控制时间轴，达到暂停录制，继续录制的效果
                    long pauseTimeLength = PauseManager.getInstance().getPauseTimeLength();
                    long currentTimeUs = PauseManager.getMicTime() - pauseTimeLength;
                    codec.queueInputBuffer(inputIndex, 0, outData.length, currentTimeUs, 0);
                }
            }

            @Override
            public void onOutputBufferAvailable(MediaCodec codec, int outIndex, MediaCodec.BufferInfo bufferInfo) {
                synchronized (isStartLock) {
                    if (!isStart) {
                        return;
                    }
                    if (bufferInfo.presentationTimeUs < PauseManager.getInstance().getLastPauseTime()) {
                    } else {
                        ByteBuffer outputBuffer = codec.getOutputBuffer(outIndex);
                        outputBuffer.position(bufferInfo.offset);
                        outputBuffer.limit(bufferInfo.offset + bufferInfo.size);

                        MuxerData muxerData = new MuxerData();
                        muxerData.setTrackIndex(MuxerManager.TRACK_AUDIO);
                        muxerData.setBufferInfo(bufferInfo);
                        muxerData.setByteBuf(outputBuffer);

                        Date date = new Date();
                        date.setTime(bufferInfo.presentationTimeUs / 1000);
                        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA);
                        String format = dateFormat.format(date);
                        Logger1.i("onSendBufferAudio", format);

                        MuxerManager.getInstance().sendWriteAudioDataMsg(muxerData);
                    }
                    // 注意，必须执行releaseOutputBuffer
                    codec.releaseOutputBuffer(outIndex, false);
                }
            }

            @Override
            public void onError(MediaCodec codec, MediaCodec.CodecException e) {
                Log.e(TAG, "AudioCodecOnError", e);
            }

            @Override
            public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) {
                Log.d(TAG, "readOutputData: " + codec.getOutputFormat());
                MuxerManager.getInstance().sendAddTrackMsg(MuxerManager.TRACK_AUDIO, format);
            }
        };
    }

    public MediaFormat getMediaFormat() {
        return mMediaFormat;
    }

    public void start() {
        //必须在mMediaCodec指定的handler中执行
        mMediaCodec.start();
        mRecorder.startRecording();

        synchronized (isStartLock) {
            isStart = true;
        }

        Runnable runnable = () -> {
            Process.setThreadPriority(Process.THREAD_PRIORITY_AUDIO);
            runRecord();
        };
        new Thread(runnable).start();
    }

    /**
     * 停止录制
     */
    public void stop() {
        synchronized (isStartLock) {
            isStart = false;
        }
        frameBytes.clear();
        mRecorder.stop();
        mMediaCodec.stop();
        mMediaCodec.release();
    }

}
